//
//  LBProgram.cpp
//  OpenGLGame
//
//  Created by Nguyen Cong Huy on 11/12/12.
//  Copyright (c) 2012 UIT. All rights reserved.
//

#include <iostream>
#include "LBProgram.h"
#include "LBUtils.h"
#include "LBGraphics.h"

LBProgram::LBProgram(const char* vShaderString, const char* fShaderString){
    initialized = false;
    program = glCreateProgram();
    
    if(!compileShader(&vertShader, GL_VERTEX_SHADER, vShaderString)){
        printf("Failed to compile vertex shader\n");
    }
    
    // Create and compile fragment shader
    if (!compileShader(&fragShader, GL_FRAGMENT_SHADER, fShaderString)){
        printf("Failed to compile fragment shader\n");
    }
    
    glAttachShader(program, vertShader);
    glAttachShader(program, fragShader);
    
    link();
}

bool LBProgram::compileShader(GLuint* shader, GLenum type, const char* shaderString){
    GLint status;
    const GLchar *source = shaderString;
    
    if (!source)
    {
        printf("Failed to load vertex shader\n");
        return false;
    }
    
    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);
    
    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
    
	if (status != true)
	{
		GLint logLength;
		glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
		if (logLength > 0)
		{
			GLchar *log = (GLchar *)malloc(logLength);
			glGetShaderInfoLog(*shader, logLength, &logLength, log);
			printf("Shader compile log:\n%s", log);
			free(log);
		}
	}	
	
    return status == true;
}

GLuint LBProgram::attributeIndex(const char* attributeName){
    return glGetAttribLocation(program, attributeName);
}

GLuint LBProgram::uniformIndex(const char* uniformName){
    return glGetUniformLocation(program, uniformName);
}

void LBProgram::use(){
    glUseProgram(program);
}

void LBProgram::validate(){
    GLint logLength;
	
	glValidateProgram(program);
	glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
	if (logLength > 0)
	{
		GLchar *log = (GLchar *)malloc(logLength);
		glGetProgramInfoLog(program, logLength, &logLength, log);
		printf("Program validate log:\n%s", log);
		free(log);
	}
}

bool LBProgram::link(){
    GLint status;
    
    glLinkProgram(program);
    
    glGetProgramiv(program, GL_LINK_STATUS, &status);
    if (status == GL_FALSE){
        GLint infoLen = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
        if(infoLen > 1){
            char* infoLog = (char*)malloc(sizeof(char)*infoLen);
            glGetProgramInfoLog(program, infoLen, 0, infoLog);
            printf("LINK PROGRAM LOG:\n%s", infoLog);
            free(infoLog);
            glDeleteProgram(program);
        }
        return false;
    }
        
    
    if (vertShader){
        glDeleteShader(vertShader);
        vertShader = 0;
    }
    
    if (fragShader){
        glDeleteShader(fragShader);
        fragShader = 0;
    }
    
    initialized = true;
    
    return true;
}

LBProgram::~LBProgram(){
    glDeleteProgram(program);
}
